package org.centny.bb;

import java.util.ArrayList;
import java.util.Random;

import org.centny.bb.Gaming.GRes;

/**
 * the BaccaraBuilder.
 * 
 * @author Scorpion
 * 
 */
public class BaccaratBuilder {
	/**
	 * one poker card number count.
	 */
	public static final int POKER_CNUM_COUNT = 13;
	/**
	 * one poker card type count.
	 */
	public static final int POKER_CTYPE_COUNT = 4;
	/**
	 * all random cards.
	 */
	private ArrayList<Byte> cards = new ArrayList<Byte>();
	/**
	 * the count of cards.
	 */
	private int cardsCount;

	/**
	 * all adding
	 */
	private ArrayList<Gaming> gamings = new ArrayList<Gaming>();

	/**
	 * the default constructor.
	 */
	public BaccaratBuilder() {
	}

	/**
	 * call to shuffle all cards.
	 */
	public void shuffle() {
		this.cards.clear();
		ArrayList<Byte> ncards = new ArrayList<Byte>();
		for (int t = 0; t < POKER_CTYPE_COUNT; t++) {
			for (int n = 0; n < POKER_CNUM_COUNT; n++) {
				int v = ((n << 3) + t);
				for (int i = 0; i < this.cardsCount; i++) {
					ncards.add((byte) v);
				}
			}
		}
		Random rd = new Random();
		while (ncards.size() > 0) {
			int i = rd.nextInt(ncards.size());
			this.cards.add(ncards.get(i));
			ncards.remove(i);
		}
	}

	/**
	 * adding
	 * 
	 * @param g
	 *            target
	 */
	public void follow(Gaming g) {
		if (g == null) {
			throw new RuntimeException("Gaming is null");
		}
		this.gamings.add(g);
	}

	/**
	 * leave one
	 * 
	 * @param g
	 *            target
	 */
	public void leave(Gaming g) {
		if (g == null) {
			throw new RuntimeException("Gaming is null");
		}
		this.gamings.remove(g);
	}

	/**
	 * running
	 */
	public void gaming() {
		if (this.cards.size() < 6) {
			this.shuffle();
		}
		ArrayList<Byte> p_card = new ArrayList<Byte>();
		ArrayList<Byte> b_card = new ArrayList<Byte>();
		p_card.add(this.applyCard());
		b_card.add(this.applyCard());
		p_card.add(this.applyCard());
		b_card.add(this.applyCard());
		int tp = 0, tb = 0;
		tp = residue(p_card);
		tb = residue(b_card);
		if (tp > 7 || tb > 7) {
			this.gaming(new GRes(p_card, b_card));
			return;
		}
		if (tp > 5 && tb > 5) {
			this.gaming(new GRes(p_card, b_card));
			return;
		}
		if (tp == 6) {
			if (tb < 6) {
				b_card.add(this.applyCard());
				tb = residue(b_card);
			}
			this.gaming(new GRes(p_card, b_card));
			return;
		}
		p_card.add(this.applyCard());
		tp = residue(p_card);
		int thr_c = p_card.get(2) >> 3;
		if (thr_c > 9) {
			thr_c = 0;
		}
		if (tb < 3) {
			b_card.add(this.applyCard());
			tb = residue(b_card);
			this.gaming(new GRes(p_card, b_card));
			return;
		}
		switch (tb) {
		case 7:
			this.gaming(new GRes(p_card, b_card));
			return;
		case 6:
			if (thr_c == 6 || thr_c == 7) {
				b_card.add(this.applyCard());
				tb = residue(b_card);
			}
			this.gaming(new GRes(p_card, b_card));
			return;
		case 5:
			if (thr_c > 3 && thr_c < 8) {
				b_card.add(this.applyCard());
				tb = residue(b_card);
			}
			this.gaming(new GRes(p_card, b_card));
			return;
		case 4:
			if (thr_c > 1 && thr_c < 8) {
				b_card.add(this.applyCard());
				tb = residue(b_card);
			}
			this.gaming(new GRes(p_card, b_card));
			return;
		case 3:
			if (thr_c != 8) {
				b_card.add(this.applyCard());
				tb = residue(b_card);
			}
			this.gaming(new GRes(p_card, b_card));
			return;
		}
		// System.out.println("error");
		// it is error code when running to here.
		throw new RuntimeException("code error");
	}

	/**
	 * run all gaming listener.
	 * 
	 * @param res
	 *            gaming result.
	 */
	private void gaming(GRes res) {
		for (Gaming g : this.gamings) {
			g.gaming(res);
		}
	}

	/**
	 * apply one card.
	 * 
	 * @return one card.
	 */
	private Byte applyCard() {
		Byte b = this.cards.get(0);
		this.cards.remove(0);
		return b;
	}

	/**
	 * @return the poker count.
	 */
	public int getCardsCount() {
		return cardsCount;
	}

	/**
	 * set the poker count.
	 * 
	 * @param cardsCount
	 *            the poker count.
	 */
	public void setCardsCount(int cardsCount) {
		this.cardsCount = cardsCount;
		this.shuffle();
	}

	/**
	 * check two cards have one pair card.
	 * 
	 * @param p_card
	 *            one cards.
	 * @param b_card
	 *            one cards
	 * @return if it have one pair.
	 */
	public static Boolean isPair(ArrayList<Byte> p_card, ArrayList<Byte> b_card) {
		return (p_card.get(0) >> 3) == (p_card.get(1) >> 3)
				|| (b_card.get(0) >> 3) == (b_card.get(1) >> 3);
	}

	/**
	 * count the residue to one cards.
	 * 
	 * @param cards
	 *            target cards.
	 * @return the residue.
	 */
	public static int residue(ArrayList<Byte> cards) {
		int total = 0;
		for (Byte b : cards) {
			int v = (b >> 3) + 1;
			if (v > 9) {
				v = 0;
			}
			total += v;
		}

		return total % 10;
	}

	/**
	 * get the card info
	 * 
	 * @param c
	 *            card byte.
	 * @return card info.
	 */
	public static int[] card(byte c) {
		int[] cinfo = new int[2];
		cinfo[0] = c >> 3;
		cinfo[1] = c & 7;
		return cinfo;
	}

}
